"""
菜单和权限管理
"""
import logging

from django.urls import reverse
from django.db.models import Count
from django.forms.formsets import formset_factory

from oracle.forms import menu
from oracle.views import tools
from oracle.service.routers import RouterHandler
from oracle.models import OMenu, OPermission, OIcon
from oracle.base.baseview import judge_render_flag, get_context_data, render_handler

logger = logging.getLogger(__name__)


def menu_list(request):
    """
    获取菜单信息以及菜单页面异步加载节点
    :param request:
    :return:
    """
    data = {
        "operation": "list",
        "type": "menu",
        "menu": {"order": list()},
    }
    # 默认设定状态为成功，失败覆盖状态为失败状态
    status = "success"
    template = None
    force_external_render = None

    # 获取菜单加载类型
    # [(None, "获取菜单页面")，(0, "菜单树加载")， (1, "叶子节点请求包含的按钮链接")]
    node_load_flag = request.GET.get("load", None)
    # 确定加载菜单的节点索引
    node_key = request.GET.get("key", None)

    # 菜单树加载
    if node_load_flag == 0 or node_load_flag == "0":
        # 异步请求全部返回json数据
        force_external_render = "external"
        """
        返回每一个菜单节点的数据格式如下：
        {
            # 节点Key: 解释 -> 页面效果(菜单列表页)
            "name": 名称 -> 显示名称，会和其余字段组合进行显示
            "isParent": 是否是父节点 -> 显示为目录，可以有子菜单节点
            "hasChildren": 是否有子菜单，父节点不一定有子节点 -> 鼠标在节点上时是否显示新增、删除图标 
            "childrenNum": 含有子节点数量 -> 如果是父节点会显示子节点数量
            "innerNum": 按钮粒度链接数量 -> 如果是叶子节点会显示按钮粒度数量
            "key": 唯一索引值 -> 无效果
            "show_icon": 显示的图标类名 -> 打开二级面板或三级面板时会显示在页面上图标栏中
            "permission": 权限的url -> 打开二级面板或三级面板时会显示在页面上URL栏中
            "permission_name": 权限的别名 -> 打开二级面板或三级面板时会显示在页面上CODE栏中
        }
        """
        # 虚拟根节点加载
        if node_key is None:
            # 只保留最简单结构
            menu_tree = [
                {"title": "菜单列表(和页面菜单层级相同)", "link_to_permission": None, "key": "root", "children_num": 100, }
            ]
        # 虚拟根节点加载顶级节点
        elif node_key == "root":
            menu_tree = OMenu.objects.filter(parent__isnull=True, menu_inner__isnull=True, status=0) \
                .annotate(inner_num=Count("menu_inner_parent"), children_num=Count("menu_node_parent")) \
                .values("key", "title", "link_to_permission__url", "link_to_permission__name", "inner_num",
                        "children_num", "menu_icon")
        # 其他节点加载子节点信息
        else:
            menu_tree = OMenu.objects.filter(parent__key=node_key, status=0) \
                .annotate(inner_num=Count("menu_inner_parent"), children_num=Count("menu_node_parent")) \
                .values("key", "title", "link_to_permission__url", "link_to_permission__name", "inner_num",
                        "children_num", "menu_icon")

        if menu_tree:
            # 数组保存节点顺序
            for item in menu_tree:
                # 保存节点key 通过key保存顺序 查找时通过key查找value
                data["menu"]["order"].append(item.get("key"))
                # 整理value信息
                data["menu"][item.get("key")] = {
                    "name": item.get("title"),
                    # 不关联权限就是一个可以含有子节点的上层节点，不论当前是否有子节点
                    "isParent": False if item.get("link_to_permission__url") else True,
                    # 有子节点数量，说明有子节点
                    "hasChildren": True if item.get("children_num") else False,
                    # 子节点数量
                    "childrenNum": item.get("children_num"),
                    # 按钮粒度链接数量
                    "innerNum": item.get("inner_num"),
                    "key": item.get("key"),
                    "show_icon": item.get("menu_icon"),
                    "permission": item.get("link_to_permission__url"),
                    "permission_name": item.get("link_to_permission__name"),
                }
        else:
            status = "menu_node_not_exist"
    elif node_load_flag == 1 or node_load_flag == "1":
        # 异步请求全部返回json数据
        force_external_render = "external"
        menu_tree = OMenu.objects.filter(menu_inner__key=node_key, status=0) \
            .values("key", "title", "link_to_permission__url", "link_to_permission__name")
        if menu_tree:
            data["menu"] = list()
            for item in menu_tree:
                data["menu"].append({
                    "key": item["key"],
                    "title": item["title"],
                    "permission": item["link_to_permission__url"],
                    "permission_name": item["link_to_permission__name"],
                })
        else:
            status = "menu_node_not_exist"
    else:
        template = "oracle/menu_list.html"

    kwargs = {"render": force_external_render}
    get_context_data(request, logger, data)
    return render_handler(request, status, template=template, data=data, **kwargs)


def menu_add(request):
    """
    新建菜单
    :param request:
    :return:
    """
    data = {
        "operation": "add",
        "type": "menu",
    }
    status = "success"
    template = None

    if request.method == "GET":
        return_url = reverse("oracle:menu_list")
        form = menu.MenuCreateForm()
        data.update({
            "form": form,
            "form_name": "新建菜单",
            "form_description": "",
            "return_url": return_url,
        })
        # 是否强制外部渲染标志
        force_external_render = judge_render_flag(request, render=None)

        if force_external_render == 0:
            template = "oracle/menu_change.html"
        else:
            # 将form类型处理成为json能格式化的数据
            tools.form_object_to_json(data)
    elif request.method == "POST":
        # 验证form
        form = menu.MenuCreateForm(request.POST)
        # form合法
        if form.is_valid():
            # 获取form返回的实例，key需要单独生成，所以目前先不进行保存
            obj = form.save(commit=False)
            key = request.POST.get("key")
            key = tools.get_only_key(OMenu, key)
            # 定义唯一key并进行保存
            obj.key = key
            obj.save()

            template = "oracle/menu_success.html"
        # 校验没有通过
        else:
            return_url = reverse("oracle:menu_list")
            # 设置错误信息，如果外部渲染传给前端
            data.update({
                "form": form,
                "form_name": "新建菜单",
                "form_description": "",
                "return_url": return_url,
            })
            status = "model_valid_check_failed"

            # 是否强制外部渲染标志
            force_external_render = judge_render_flag(request, render=None)

            if force_external_render == 0:
                template = "oracle/menu_change.html"
            else:
                # 将form类型处理成为json能格式化的数据
                tools.form_object_to_json(data)

                for item in data["form"]:
                    key = data["form"][item]["name"]
                    if key in request.POST:
                        data["form"][item]["instance"] = request.POST.get(key)
    # 非get或者post方法访问进行友好报错处理
    else:
        status = "method_not_allow"
    get_context_data(request, logger, data)
    return render_handler(request, status, template=template, data=data)


def menu_edit(request, key):
    """
    修改菜单
    :param request:
    :param key: 修改菜单索引值
    :return:
    """
    data = {
        "operation": "change",
        "type": "menu",
    }
    status = "success"
    template = None

    # 获取要修改的实例信息
    obj = OMenu.objects.filter(key=key, status=0).first()

    if request.method == "GET":
        # 如果有实例则返回修改页面信息
        if obj:
            return_url = reverse("oracle:menu_list")
            form = menu.MenuUpdateForm(instance=obj)
            data.update({
                "form": form,
                "form_name": "修改菜单",
                "form_description": "",
                "return_url": return_url,
            })
            # 是否强制外部渲染标志
            force_external_render = judge_render_flag(request, render=None)

            if force_external_render == 0:
                template = "oracle/menu_change.html"
            else:
                # 将form类型处理成为json能格式化的数据
                tools.form_object_to_json(data)
        # 没有实例则返回人性化错误信息
        else:
            # 设置错误信息，如果外部渲染传给前端
            status = "model_object_not_exist"
    elif request.method == "POST":
        if obj:
            post_data = dict()
            for key in menu.MenuUpdateForm.base_fields:
                if key in request.POST:
                    post_data[key] = request.POST.get(key)
                else:
                    post_data[key] = getattr(obj, key)
            # 验证form
            form = menu.MenuUpdateForm(instance=obj, data=post_data)
            # form合法
            if form.is_valid():
                form.save()
                template = "oracle/menu_success.html"
            # 校验没有通过
            else:
                return_url = reverse("oracle:menu_list")
                # 设置错误信息，如果外部渲染传给前端
                data.update({
                    "form": form,
                    "form_name": "修改菜单",
                    "form_description": "",
                    "return_url": return_url,
                })
                status = "model_valid_check_failed"

                # 是否强制外部渲染标志
                force_external_render = judge_render_flag(request, render=None)

                if force_external_render == 0:
                    template = "oracle/menu_change.html"
                else:
                    # 将form类型处理成为json能格式化的数据
                    tools.form_object_to_json(data)

                    for item in data["form"]:
                        key = data["form"][item]["name"]
                        if key in request.POST:
                            data["form"][item]["instance"] = request.POST.get(key)

        # 没有实例则返回人性化错误信息
        else:
            # 设置错误信息，如果外部渲染传给前端
            status = "model_object_not_exist"
    # 非get或者post方法访问进行友好报错处理
    else:
        status = "method_not_allow"

    get_context_data(request, logger, data)
    return render_handler(request, status, template=template, data=data)


def menu_del(request, key):
    """
    删除菜单
    :param request:
    :param key: 删除菜单索引值
    :return:
    """
    data = {
        "operation": "delete",
        "type": "menu",
    }
    status = "success"
    template = None
    msg = None

    # 获取要删除的实例信息
    obj = OMenu.objects.filter(key=key, status=0).first()
    if request.method == "GET":
        # 如果有实例则返回修改页面信息
        if obj:
            return_url = reverse("oracle:menu_list")
            data.update({
                "object": obj.title,
                "form_name": "删除菜单",
                "form_description": "",
                "return_url": return_url,
            })

            # 定义内部渲染函数
            template = "oracle/delete_confirm.html"
        # 没有实例则返回人性化错误信息
        else:
            # 设置错误信息，如果外部渲染传给前端
            status = "model_object_not_exist"
    elif request.method == "POST":
        if obj:
            # 没有子节点以及按钮链接权限则删除菜单
            if (obj.menu_node_parent.count() == 0) and (obj.menu_inner_parent.count() == 0):
                obj.delete()

                template = "oracle/menu_success.html"
            # 有权限无法删除节点
            else:
                msg = "节点含有子菜单或者按钮链接，请先把其余节点删除后再删除本节点。"
                status = "model_object_can_not_delete"
        # 没有实例则返回人性化错误信息
        else:
            # 设置错误信息，如果外部渲染传给前端
            status = "model_object_not_exist"
    # 非get或者post方法访问进行友好报错处理
    else:
        status = "method_not_allow"
    get_context_data(request, logger, data)
    return render_handler(request, status, template=template, data=data, msg=msg)


def menu_icon_list(request):
    """
    菜单图标列表
    :param request:
    :return:
    """
    data = {
        "operation": "list",
        "type": "menu",
    }
    status = "success"
    template = "oracle/icon_list.html"

    # TODO 函数未进行测试
    obj = OIcon.objects.filter(category__isnull=True, status=0)
    data["top_icons"] = list()
    for obj in obj:
        top_icon = {"title": obj.title, "icon_class": obj.icon_class, "children": []}
        for item in obj.oicon_set.all():
            top_icon["children"].append({"title": item.title, "icon_class": item.icon_class})
        data["top_icons"].append(top_icon)

    get_context_data(request, logger, data)
    return render_handler(request, status, template=template, data=data)


def permission_add(request):
    """
    新建权限
    :param request:
    :return:
    """
    data = {
        "operation": "add",
        "type": "permission",
    }
    status = "success"
    template = None

    # 异步请求全部返回json数据
    if request.method == "GET":
        return_url = reverse("oracle:menu_list")
        form = menu.PermissionCreateForm()
        data.update({
            "form": form,
            "form_name": "添加权限",
            "form_description": "",
            "return_url": return_url,
        })
        # 是否强制外部渲染标志
        force_external_render = judge_render_flag(request, render=None)

        if force_external_render == 0:
            template = "oracle/menu_change.html"
        else:
            # 将form类型处理成为json能格式化的数据
            tools.form_object_to_json(data)
    elif request.method == "POST":
        # 验证form
        form = menu.PermissionCreateForm(request.POST)
        # form合法
        if form.is_valid():
            # 获取form返回的实例，key需要单独生成，所以目前先不进行保存
            obj = form.save(commit=False)
            key = request.POST.get("key")
            key = tools.get_only_key(OPermission, key)
            # 定义唯一key并进行保存
            obj.key = key
            obj.save()

            template = "oracle/menu_success.html"
        # 校验没有通过
        else:
            return_url = reverse("oracle:menu_list")
            # 设置错误信息，如果外部渲染传给前端
            data.update({
                "form": form,
                "form_name": "添加权限",
                "form_description": "",
                "return_url": return_url,
            })
            status = "model_valid_check_failed"

            # 是否强制外部渲染标志
            force_external_render = judge_render_flag(request, render=None)

            if force_external_render == 0:
                template = "oracle/menu_change.html"
            else:
                # 将form类型处理成为json能格式化的数据
                tools.form_object_to_json(data)

                for item in data["form"]:
                    key = data["form"][item]["name"]
                    if key in request.POST:
                        data["form"][item]["instance"] = request.POST.get(key)

    # 非get或者post方法访问进行友好报错处理
    else:
        status = "method_not_allow"
    get_context_data(request, logger, data)
    return render_handler(request, status, template=template, data=data)


def permission_edit(request, key):
    """
    修改权限
    :param request:
    :param key:
    :return:
    """
    data = {
        "operation": "change",
        "type": "permission",
    }
    status = "success"
    template = None

    # 获取要修改的实例信息
    if request.method == "GET":
        search_type = request.GET.get("type")
    elif request.method == "POST":
        search_type = request.POST.get("type")
    else:
        search_type = None
    if search_type == "permission":
        obj = OPermission.objects.filter(key=key, status=0).first()
    else:
        obj = OPermission.objects.filter(omenu__key=key, status=0).first()

    if request.method == "GET":
        # 如果有实例则返回修改页面信息
        if obj:
            return_url = reverse("oracle:menu_list")
            form = menu.PermissionUpdateForm(instance=obj)
            data.update({
                "form": form,
                "form_name": "修改权限",
                "form_description": "",
                "return_url": return_url,
            })
            # 是否强制外部渲染标志
            force_external_render = judge_render_flag(request, render=None)

            if force_external_render == 0:
                template = "oracle/menu_change.html"
            else:
                # 将form类型处理成为json能格式化的数据
                tools.form_object_to_json(data)
        # 没有实例则返回人性化错误信息
        else:
            # 设置错误信息，如果外部渲染传给前端
            status = "model_object_not_exist"
    elif request.method == "POST":
        if obj:
            post_data = dict()
            for key in menu.PermissionUpdateForm.base_fields:
                if key in request.POST:
                    post_data[key] = request.POST.get(key)
                elif key == "rel":
                    post_data[key] = obj.omenu.key
                else:
                    post_data[key] = getattr(obj, key)

            # 验证form
            form = menu.PermissionUpdateForm(instance=obj, data=post_data)
            # form合法
            if form.is_valid():
                # 获取form返回的实例，key需要单独生成，所以目前先不进行保存
                form.save()
                template = "oracle/menu_success.html"
            # 校验没有通过
            else:
                return_url = reverse("oracle:menu_list")
                # 设置错误信息，如果外部渲染传给前端
                data.update({
                    "form": form,
                    "form_name": "修改权限",
                    "form_description": "",
                    "return_url": return_url,
                })
                status = "model_valid_check_failed"
                # 是否强制外部渲染标志
                force_external_render = judge_render_flag(request, render=None)

                if force_external_render == 0:
                    template = "oracle/menu_change.html"
                else:
                    # 将form类型处理成为json能格式化的数据
                    tools.form_object_to_json(data)

                    for item in data["form"]:
                        key = data["form"][item]["name"]
                        if key in request.POST:
                            data["form"][item]["instance"] = request.POST.get(key)
        # 没有实例则返回人性化错误信息
        else:
            # 设置错误信息，如果外部渲染传给前端
            status = "model_object_not_exist"
    # 非get或者post方法访问进行友好报错处理
    else:
        status = "method_not_allow"
    get_context_data(request, logger, data)
    return render_handler(request, status, template=template, data=data)


def permission_delete_handler(request, obj, is_multi=False):
    """"
    权限删除处理，单独操作和批量操作提取的公共处理方法
    :param request: 请求变量
    :param obj: 删除实例
    :param is_multi: 是否是批量操作，批量操作传入可迭代对象，目前是queryset
    :return:
    """
    data = {
        "operation": "delete",
        "type": "permission",
    }
    status = "success"
    template = None
    force_external_render = None

    if request.method == "GET":
        if is_multi:
            # 批量删除只提供post方法删除
            status = "method_not_allow"
        # 如果有实例则返回修改页面信息
        elif obj:
            return_url = reverse("oracle:menu_list")
            data.update({
                "object": obj.title,
                "form_name": "删除权限",
                "form_description": "",
                "return_url": return_url,
            })
            template = "oracle/delete_confirm.html"
        # 没有实例则返回人性化错误信息
        else:
            # 设置错误信息，如果外部渲染传给前端
            status = "model_object_not_exist"
    elif request.method == "POST":
        # 设置外部渲染，只返回json数据
        force_external_render = "external"
        if obj:
            if is_multi:
                # 有对应权限的菜单进行主动更新，应该是配置了级联删除方式，保险起见手动更新
                OMenu.objects.filter(link_to_permission__in=obj, status=0).update(link_to_permission=None)
                obj.delete()
            else:
                # 有对应权限的菜单进行主动更新，应该是配置了级联删除方式，保险起见手动更新
                OMenu.objects.filter(link_to_permission=obj, status=0).update(link_to_permission=None)
                obj.delete()
        # 没有实例则返回人性化错误信息
        else:
            # 设置错误信息，如果外部渲染传给前端
            status = "model_object_not_exist"
    # 非get或者post方法访问进行友好报错处理
    else:
        status = "method_not_allow"
    kwargs = {"render": force_external_render}
    get_context_data(request, logger, data)
    return render_handler(request, status, template=template, data=data, **kwargs)


def permission_delete(request, key):
    """
    删除权限
    :param request:
    :param key: 删除权限索引值
    :return:
    """
    # 获取要删除的实例信息
    if request.method == "GET":
        search_type = request.GET.get("type")
    elif request.method == "POST":
        search_type = request.POST.get("type")
    else:
        search_type = None
    if search_type == "permission":
        obj = OPermission.objects.filter(key=key, status=0).first()
    else:
        obj = OPermission.objects.filter(omenu__key=key, status=0).first()
    return permission_delete_handler(request, obj)


def multi_permission_delete(request):
    """
    删除批量权限
    :param request:
    :return:
    """
    # 删除权限索引值
    keys = request.POST.getlist("key")
    # 获取要删除的实例信息
    obj = OPermission.objects.filter(key__in=keys, status=0)
    return permission_delete_handler(request, obj, is_multi=True)


def generate_multi_formset():
    """
    根据项目中所有注册的url信息生成 formset 信息
    :return:
    """
    # 获取项目中所有的URL
    router = RouterHandler()
    all_url_dict = router.get_all_url()

    # 对比项目中url和数据库中的url生成对应权限
    # 1、 项目中有而数据库中没有 ，批量添加
    # 2、 项目中没有数据库中有 , 批量删除
    # 3、 项目中有数据库中也有，但是 url 不一致，提示更新对应信息

    # 获取项目中所有的url的name
    all_url_name_set = set(all_url_dict.keys())
    all_url_name_set.discard("_order")

    # 获取数据库中所有的url的name
    permissions = OPermission.objects.all().values("key", "title", "name", "url", "status", "level")
    permissions_order = list()
    permissions_dict = dict()
    permissions_name_set = set()
    for row in permissions:
        # 保存查找到的permission顺序，之后会使用权限名字的集合类型计算包含关系等，会破坏顺序结构，在此保存
        permissions_order.append(row["name"])
        # 按名字存储权限信息，方便之后查找使用
        permissions_dict[row["name"]] = row
        # 权限名字的集合，方便之后做集合计算操作
        permissions_name_set.add(row["name"])

    # 确认数据库中存储的数据与项目数据是否一致，不一致修改url为提示
    for name in permissions_order:
        if name not in all_url_name_set:
            continue
        url_pattern = all_url_dict.get(name)
        if permissions_dict[name]["url"] != url_pattern["url"]:
            permissions_dict[name]["url"] = "同名网址获取实际url和数据库中的不一致"

    # 获得应该添加、删除、修改的权限的列表
    # 增加的列表
    # 项目中多的批量添加
    # 实际项目比数据库中多的url信息
    generate_name_set = all_url_name_set - permissions_name_set
    generate_formset_class = formset_factory(menu.MultiAddPermissionForm, extra=0)
    initial_list = list()
    # 根据批量添加的名字确定项目列表
    for name in all_url_dict["_order"]:
        if name in generate_name_set:
            initial_list.append(all_url_dict[name])
    generate_formset = generate_formset_class(initial=initial_list)

    # 删除的列表
    # 数据库中比实际项目多的url信息，应该删除存储
    delete_name_set = permissions_name_set - all_url_name_set
    delete_row_list = list()
    for name in permissions_order:
        if name in delete_name_set:
            delete_row_list.append(permissions_dict[name])

    # 更新的列表
    # 数据库中存在并且实际项目也存在的就可以酌情修改了
    update_name_set = permissions_name_set & all_url_name_set
    update_formset_class = formset_factory(menu.MultiEditPermissionForm, extra=0)
    initial_list = list()
    # 根据批量添加的名字确定项目列表
    for name in permissions_order:
        if name in update_name_set:
            value = permissions_dict[name]
            value["id"] = value["key"]
            initial_list.append(value)
    update_formset = update_formset_class(initial=initial_list)
    return generate_formset, delete_row_list, update_formset


def multi_permission_add(request):
    """
    批量操作权限
    :param request:
    :return:
    """
    data = {
        "operation": "add",
        "type": "multi_permission",
    }
    status = "success"
    template = None
    force_external_render = None

    if request.method == "GET":
        return_url = reverse("oracle:menu_list")
        generate_formset, delete_row_list, update_formset = generate_multi_formset()
        data.update({
            "generate_formset": generate_formset,
            "delete_row_list": delete_row_list,
            "update_formset": update_formset,
            "form_name": "批量添加权限",
            "form_description": "",
            "return_url": return_url,
        })
        # 是否强制外部渲染标志
        external_render = judge_render_flag(request, render=None)

        if external_render == 0:
            template = "oracle/multi_permissions.html"
        else:
            formset = {"formset": data["generate_formset"]}
            # 将form类型处理成为json能格式化的数据
            tools.formset_object_to_json(formset)
            data["generate_formset"] = formset["formset"]
            formset = {"formset": data["update_formset"]}
            tools.formset_object_to_json(formset)
            data["update_formset"] = formset["formset"]

    elif request.method == "POST":
        # 获取是什么类型的操作
        post_type = request.POST.get("type")
        # 设置外部渲染，只返回json数据
        force_external_render = "external"
        # 传入form是否校验失败标志位
        # 批量添加权限
        if post_type == "generate":
            data.update({
                "operation": "add",
                "type": "multi_permission",
            })
            # 创建实例，验证表单
            generate_formset_class = formset_factory(menu.MultiAddPermissionForm)
            generate_formset = generate_formset_class(data=request.POST)
            # 如果表单合法
            if generate_formset.is_valid():
                # 可以提交数据的列表
                object_list = list()
                # 是否错误标志位
                no_error = True
                # 获取合法数据
                post_row_list = generate_formset.cleaned_data
                # 循环表单
                for i in range(0, generate_formset.total_form_count()):
                    # 获取第i个表单的数据
                    row_dict = post_row_list[i]
                    new_object_data = dict()
                    try:
                        # 创建model实例
                        new_object_data["title"] = row_dict.get("title")
                        new_object_data["url"] = row_dict["url"]
                        new_object_data["name"] = row_dict["name"]
                        new_object_data["status"] = row_dict["status"]
                        new_object_data["level"] = row_dict["level"]
                        keys = list(new_object_data.keys())
                        for name in keys:
                            if not new_object_data[name]:
                                del new_object_data[name]
                        new_object = OPermission(**new_object_data)
                        # 验证唯一性
                        new_object.validate_unique()
                        key = row_dict.get("key")
                        key = tools.get_only_key(OPermission, key)
                        new_object.key = key
                        # 确认没有问题加入列表，之后作为整体进行提交
                        object_list.append(new_object)
                    except Exception as e:
                        # 报错则在对应form记录错误信息
                        generate_formset[i].add_error(None, e)
                        # 标志位为假
                        no_error = False
                        status = "formset_check_invalid"
                # 如果标志位为真，说明没有错误，整体提交批量数据
                if no_error:
                    # 批量提交创建权限
                    OPermission.objects.bulk_create(object_list, batch_size=100)
                    # 清空formset
                    generate_formset = None
            else:
                # 验证失败
                status = "formset_check_invalid"

            # 如果有fromset说明验证失败，返回error信息
            if generate_formset:
                data["formset"] = generate_formset
                # 将form类型处理成为json能格式化的数据
                tools.formset_object_to_json(data)
                for form_index in range(len(data["formset"])):
                    form = data["formset"][form_index]
                    for item in form:
                        key = f"form-{form_index}-{form[item]['name']}"
                        if key in request.POST:
                            form[item]["instance"] = request.POST.get(key)
                        else:
                            form[item]["instance"] = form[item]["initial"]
        # 批量更新
        elif post_type == "update":
            data.update({
                "operation": "change",
                "type": "multi_permission",
            })
            count = 0
            form_dict = dict()
            while f"form-{count}-id" in request.POST:
                # 每个form的id存储最原始的key值
                key = request.POST.get(f"form-{count}-id")
                # 记录count信息
                form_dict[key] = {"count": count}
                # 记录所有post传过来的值
                for k in menu.MultiEditPermissionForm.base_fields:
                    if k == "id":
                        continue
                    elif f"form-{count}-{k}" in request.POST:
                        form_dict[key][k] = request.POST.get(f"form-{count}-{k}")
                    else:
                        form_dict[key][k] = "--"
                count += 1

            # 根据key获取所有queryset
            objs = OPermission.objects.filter(key__in=form_dict.keys())

            all_valid = True

            for obj in objs:
                # 更新post_data，如果有post没有传入的使用obj本身值代入
                post_data = form_dict[obj.key]
                for name in post_data:
                    if post_data[name] == "--":
                        post_data[name] = getattr(obj, name)
                # 构造form
                form = menu.MultiEditPermissionForm(instance=obj, data=post_data)
                if not form.is_valid():
                    all_valid = False
                # 存储每一个form信息
                form_dict[obj.key]["form"] = form

            if all_valid:
                for key in form_dict:
                    form_dict[key]["form"].save()
            else:
                data["formset"] = list()
                for key in form_dict:
                    count = form_dict[key].pop("count")
                    form = form_dict[key].pop("form")
                    context = {"operation": "change", "form": form}
                    # 将form类型处理成为json能格式化的数据
                    tools.form_object_to_json(context)
                    for item in context["form"]:
                        k = context["form"][item]["name"]
                        k = f"form-{count}-{k}"
                        if k in request.POST:
                            context["form"][item]["instance"] = request.POST.get(k)
                    data["formset"].append(context["form"])
                status = "formset_check_invalid"

    # 非get或者post方法访问进行友好报错处理
    else:
        status = "method_not_allow"

    kwargs = {"render": force_external_render}
    get_context_data(request, logger, data)
    return render_handler(request, status, template=template, data=data, **kwargs)


def generate_menu_link_formset(menu_add_formset=None, menu_edit_formset=None):
    """
    权限关联菜单formset生成
    :param menu_add_formset: 添加关联菜单formset
    :param menu_edit_formset: 修改关联菜单formset
    :return:
    """
    permission_obj = OPermission.objects.filter(omenu__isnull=True, status=0)
    initial = list()
    for item in permission_obj:
        initial.append(
            {
                "key": item.key,
                "title": item.title,
                "link_to_permission": item.key,
            }
        )
    menu_add_formset_class = formset_factory(menu.MenuLinkAddForm, extra=0)
    menu_add_formset = menu_add_formset_class(initial=initial)

    menu_obj = OMenu.objects.filter(link_to_permission__isnull=False, status=0). \
        values(
        'key', 'title', 'menu_icon', 'link_to_permission__key', 'parent__key', 'menu_inner__key', 'status', 'level'
    )
    initial = list()
    for item in menu_obj:
        initial.append(
            {
                "id": item["key"],
                "key": item["key"],
                "title": item["title"],
                "menu_icon": item["menu_icon"],
                "link_to_permission": item["link_to_permission__key"],
                "parent": item["parent__key"],
                "menu_inner": item["menu_inner__key"],
                "status": item["status"],
                "level": item["level"]
            }
        )
    menu_edit_formset_class = formset_factory(menu.MenuLinkEditForm, extra=0)
    menu_edit_formset = menu_edit_formset_class(initial=initial)
    return menu_add_formset, menu_edit_formset


def menu_link_add(request):
    """
    批量操作权限关联菜单
    :param request:
    :return:
    """
    data = {
        "operation": "add",
        "type": "menu_link_permission",
    }
    status = "success"
    template = None
    force_external_render = None
    if request.method == "GET":
        return_url = reverse("oracle:menu_list")
        menu_add_formset, menu_edit_formset = generate_menu_link_formset()

        data.update({
            "menu_add_formset": menu_add_formset,
            "menu_edit_formset": menu_edit_formset,
            "form_name": "添加角色",
            "form_description": "",
            "return_url": return_url,
        })
        # 是否强制外部渲染标志
        external_render = judge_render_flag(request, render=None)

        if external_render == 0:
            template = "oracle/menu_link.html"
        else:
            formset = {"formset": data["menu_add_formset"]}
            # 将form类型处理成为json能格式化的数据
            tools.formset_object_to_json(formset)
            data["menu_add_formset"] = formset["formset"]
            formset = {"formset": data["menu_edit_formset"]}
            tools.formset_object_to_json(formset)
            data["menu_edit_formset"] = formset["formset"]

    elif request.method == "POST":
        # 获取是什么类型的操作
        post_type = request.POST.get("type")
        # 设置外部渲染，只返回json数据
        force_external_render = "external"
        # 批量添加权限
        if post_type == "add":
            data.update({
                "operation": "add",
                "type": "menu_link_permission",
            })
            # 创建实例，验证表单
            menu_add_formset_class = formset_factory(menu.MenuLinkAddForm)
            menu_add_formset = menu_add_formset_class(data=request.POST)
            # 如果表单合法
            if menu_add_formset.is_valid():
                # 可以提交数据的列表
                object_list = list()
                # 是否错误标志位
                no_error = True
                # 获取合法数据
                post_row_list = menu_add_formset.cleaned_data
                # 循环表单
                for i in range(0, menu_add_formset.total_form_count()):
                    # 获取第i个表单的数据
                    row_dict = post_row_list[i]
                    new_object_data = dict()
                    try:
                        # new_object_data["key"] = row_dict["key"]
                        new_object_data["title"] = row_dict["title"]
                        new_object_data["menu_icon"] = row_dict["menu_icon"]
                        new_object_data["link_to_permission"] = row_dict["link_to_permission"]
                        new_object_data["parent"] = row_dict["parent"]
                        new_object_data["menu_inner"] = row_dict["menu_inner"]
                        new_object_data["status"] = row_dict["status"]
                        new_object_data["level"] = row_dict["level"]
                        keys = list(new_object_data.keys())
                        for name in keys:
                            if not new_object_data[name]:
                                del new_object_data[name]
                        # 创建model实例
                        new_object = OMenu(**new_object_data)
                        # 验证唯一性
                        new_object.validate_unique()
                        key = row_dict.get("key")
                        key = tools.get_only_key(OMenu, key)
                        new_object.key = key
                        # 确认没有问题加入列表，之后作为整体进行提交
                        object_list.append(new_object)
                    except Exception as e:
                        # 报错则在对应form记录错误信息
                        menu_add_formset[i].add_error(None, e)
                        # 标志位为假
                        no_error = False
                        status = "formset_check_invalid"
                # 如果标志位为真，说明没有错误，整体提交批量数据
                if no_error:
                    # 批量提交创建权限
                    OMenu.objects.bulk_create(object_list, batch_size=100)
                    # 清空formset
                    menu_add_formset = None
            else:
                # 验证失败
                status = "formset_check_invalid"

            # 如果有fromset说明验证失败，返回error信息
            if menu_add_formset:
                data["formset"] = menu_add_formset
                # 将form类型处理成为json能格式化的数据
                tools.formset_object_to_json(data)
                for form_index in range(len(data["formset"])):
                    form = data["formset"][form_index]
                    for item in form:
                        key = f"form-{form_index}-{form[item]['name']}"
                        if key in request.POST:
                            form[item]["instance"] = request.POST.get(key)
                        else:
                            form[item]["instance"] = form[item]["initial"]
        # 批量更新
        elif post_type == "update":
            data.update({
                "operation": "change",
                "type": "menu_link_permission",
            })
            count = 0
            form_dict = dict()
            while f"form-{count}-id" in request.POST:
                # 每个form的id存储最原始的key值
                key = request.POST.get(f"form-{count}-id")
                # 记录count信息
                form_dict[key] = {"count": count}
                # 记录所有post传过来的值
                for k in menu.MenuLinkEditForm.base_fields:
                    if k == "id":
                        continue
                    elif f"form-{count}-{k}" in request.POST:
                        form_dict[key][k] = request.POST.get(f"form-{count}-{k}")
                    else:
                        form_dict[key][k] = "--"
                count += 1

            # 根据key获取所有queryset
            objs = OMenu.objects.filter(key__in=form_dict.keys())

            all_valid = True

            for obj in objs:
                # 更新post_data，如果有post没有传入的使用obj本身值代入
                post_data = form_dict[obj.key]
                for name in post_data:
                    if post_data[name] == "--":
                        post_data[name] = getattr(obj, name)
                # 构造form
                form = menu.MenuLinkEditForm(instance=obj, data=post_data)
                if not form.is_valid():
                    all_valid = False
                # 存储每一个form信息
                form_dict[obj.key]["form"] = form

            if all_valid:
                for key in form_dict:
                    form_dict[key]["form"].save()
            else:
                data["formset"] = list()
                for key in form_dict:
                    count = form_dict[key].pop("count")
                    form = form_dict[key].pop("form")
                    context = {"operation": "change", "form": form}
                    # 将form类型处理成为json能格式化的数据
                    tools.form_object_to_json(context)
                    for item in context["form"]:
                        k = context["form"][item]["name"]
                        k = f"form-{count}-{k}"
                        if k in request.POST:
                            context["form"][item]["instance"] = request.POST.get(k)
                    data["formset"].append(context["form"])
                status = "formset_check_invalid"

    # 非get或者post方法访问进行友好报错处理
    else:
        status = "method_not_allow"

    kwargs = {"render": force_external_render}
    get_context_data(request, logger, data)
    return render_handler(request, status, template=template, data=data, **kwargs)
